#ifndef _CPP_SOURCE_CODE_GENERATOR_H_
#define _CPP_SOURCE_CODE_GENERATOR_H_

#include "CodeGenerator.h"

struct CppSourceGenerator {
	xml_node m_Types;
	fstream  m_OutputStream;
	char     m_HeaderFilePath[MAX_OUTPUT_PATH_LEN];

	CppSourceGenerator(xml_node types)
		:m_Types(types) 
	{}

	int Initial(const char* outfilepath);
	int Finalize();
	int Generate();
};


inline int CppSourceGenerator::Initial(const char* typefilepath)
{
	char outfilepath[MAX_OUTPUT_PATH_LEN] = "";
	strcpy( outfilepath, typefilepath );
	char* ptr = strrchr(outfilepath, '.');
	strcpy(ptr, ".cpp");

	char* ptr_pre0 = strrchr(outfilepath, '/');
	char* ptr_pre1 = strrchr(outfilepath, '\\');
	if( ptr_pre0 ) {
		strcpy( m_HeaderFilePath, ptr_pre0+1 );
	} else if( ptr_pre1 ){
		strcpy( m_HeaderFilePath, ptr_pre1+1 );
	} else {
		strcpy( m_HeaderFilePath, outfilepath );
	}	
	ptr = strrchr(m_HeaderFilePath, '.');
	strcpy(ptr, ".h");	

	m_OutputStream.open(outfilepath, fstream::out);
	return 0;
}

inline int CppSourceGenerator::Finalize()
{
	m_OutputStream.close();
	return 0;
}

inline int CppSourceGenerator::Generate()
{
	m_OutputStream << "#include \"ObjectSystem.h\"\n";
	m_OutputStream << "#include \"" << m_HeaderFilePath << "\"\n\n\n";

	/**
	* generate stub source file
	*/
	for( xml_node type = m_Types.first_child(); type; type=type.next_sibling() )
	{
		const char* type_name = type.attribute("type").value();
		/**
		* enum
		*/
		if( 0==strcmp(type_name, "enum") ) {
			size_t enum_item_count = 0;
			m_OutputStream << "static EnumItem s_Enum_" << type.name() << "_Items[] = {\n";
			int current_value = 0;
			for(xml_node item=type.first_child(); item; item=item.next_sibling() )
			{				
				if( item.attribute("value") ) {
					current_value =  atoi(item.attribute("value").value());
				}
				m_OutputStream << "    {\"" << item.name() << "\", " << current_value;
				m_OutputStream << (item.next_sibling()? "},\n" : "}\n");
				
				++current_value;
				++enum_item_count;
			}
			m_OutputStream << "};\n";

			m_OutputStream << "EnumTypeInfo s_Enum_" << type.name() << "_Info(\"" << type.name() << "\", " << "sizeof(" << type.name() << "), " << enum_item_count << ", " << "&s_Enum_" << type.name() << "_Items[0]);\n\n";
		} else 
        /**
		* struct
		*/
		if( 0==strcmp(type_name, "struct") ) {			
			size_t struct_item_count = 0;
			m_OutputStream << "static StructItem s_Struct_" << type.name() << "_Items[] = {\n";
			for(xml_node item=type.first_child(); item; item=item.next_sibling() )
			{				
				m_OutputStream << "    {\"" << item.name() << "\", \"" << item.attribute("type").value() << "\"";
				m_OutputStream << (item.next_sibling()? "},\n" : "}\n");
				++struct_item_count;
			}
			m_OutputStream << "};\n";

			m_OutputStream << "StructTypeInfo s_Struct_" << type.name() << "_Info(\"" << type.name() << "\", " << "sizeof(" << type.name() << "), " << struct_item_count << ", " << "&s_Struct_" << type.name() << "_Items[0]);\n\n";

		} else 
		/**
		* class
		*/
		if( 0==strcmp(type_name, "class") ) {
			// method param
			for(xml_node item=type.first_child(); item; item=item.next_sibling() )
			{
				if( 0==strcmp(item.attribute("type").value(), "function") ) 
				{
					m_OutputStream << "static MethodParam s_Class_" << type.name() << "_" << item.name() << "_Params[] = {\n";
					for( xml_node param = item.first_child(); param; param=param.next_sibling() ) {
						m_OutputStream << "    {\"" << param.name() << "\", \"" << param.attribute("type").value() << "\"";
						m_OutputStream << (param.next_sibling()? "},\n" : "}\n");
					}
					m_OutputStream << "};\n";
				}
			}			

			// method
			size_t method_item_count = 0;
			m_OutputStream << "static MethodItem s_Class_" << type.name() << "_Methods[] = {\n";
			for(xml_node item=type.first_child(); item; item=item.next_sibling() )
			{
				if( 0==strcmp(item.attribute("type").value(), "function") ) {
					m_OutputStream << "    {\"" << item.name() << "\", \"" << item.attribute("return").value() << "\""
					       << ", sizeof(" << "s_Class_" << type.name() << "_" << item.name() << "_Params" << ")/sizeof(MethodParam),"
						   << " &s_Class_" << type.name() << "_" << item.name() << "_Params[0]";
					m_OutputStream << (item.next_sibling()? "},\n" : "}\n");
					++method_item_count;
				}
			}
			m_OutputStream << "};\n";

			// property
			size_t property_item_count = 0;
			m_OutputStream << "static PropertyItem s_Class_" << type.name() << "_Propertys[] = {\n";
			for(xml_node item=type.first_child(); item; item=item.next_sibling() )
			{
				if( 0!=strcmp(item.attribute("type").value(), "function") ) { // member function
					m_OutputStream << "    {\"" << item.name() << "\", \"" << item.attribute("type").value() << "\"";
					m_OutputStream << (item.next_sibling()? "},\n" : "}\n");
					++property_item_count;
				}
			}
			m_OutputStream << "};\n";

			m_OutputStream << "ClassTypeInfo s_Class_" << type.name() << "_Info(\"" << type.name() << "\", " << "sizeof(" << type.name() << "), " << property_item_count << ", " << "&s_Class_" << type.name() << "_Propertys[0],"
				<< method_item_count << ", &s_Class_" << type.name() << "_Methods[0] );\n\n";

		} else
		/**
		* runtime class
		*/
		if( 0==strcmp(type_name, "rtclass") ) {
			// method param
			for(xml_node item=type.first_child(); item; item=item.next_sibling() )
			{
				if( 0==strcmp(item.attribute("type").value(), "function") ) 
				{
					m_OutputStream << "static MethodParam s_Class_" << type.name() << "_" << item.name() << "_Params[] = {\n";
					for( xml_node param = item.first_child(); param; param=param.next_sibling() ) {
						m_OutputStream << "    {\"" << param.name() << "\", \"" << param.attribute("type").value() << "\"";
						m_OutputStream << (param.next_sibling()? "},\n" : "}\n");
					}
					m_OutputStream << "};\n";
				}
			}			

			// method
			size_t method_item_count = 0;
			m_OutputStream << "static MethodItem s_Class_" << type.name() << "_Methods[] = {\n";
			for(xml_node item=type.first_child(); item; item=item.next_sibling() )
			{
				if( 0==strcmp(item.attribute("type").value(), "function") ) {
					m_OutputStream << "    {\"" << item.name() << "\", \"" << item.attribute("return").value() << "\""
						<< ", sizeof(" << "s_Class_" << type.name() << "_" << item.name() << "_Params" << ")/sizeof(MethodParam),"
						<< " &s_Class_" << type.name() << "_" << item.name() << "_Params[0]";
					m_OutputStream << (item.next_sibling()? "},\n" : "}\n");
					++method_item_count;
				}
			}
			m_OutputStream << "};\n";

			// property
			size_t property_item_count = 0;
			m_OutputStream << "static PropertyItem s_Class_" << type.name() << "_Propertys[] = {\n";
			for(xml_node item=type.first_child(); item; item=item.next_sibling() )
			{
				if( 0!=strcmp(item.attribute("type").value(), "function") ) { // member function
					m_OutputStream << "    {\"" << item.name() << "\", \"" << item.attribute("type").value() << "\"";
					m_OutputStream << (item.next_sibling()? "},\n" : "}\n");
					++property_item_count;
				}
			}
			m_OutputStream << "};\n";

			m_OutputStream << "RTClassTypeInfo s_RTClass_" << type.name() << "_Info(\"" << type.name() << "\", " << "sizeof(" << type.name() << "), " << property_item_count << ", " << "&s_Class_" << type.name() << "_Propertys[0],"
				<< method_item_count << ", &s_Class_" << type.name() << "_Methods[0] );\n\n";

		} else
		/**
		* basic type
		*/
		{
			m_OutputStream << "BasicTypeInfo s_Basic_" << type.name() << "_Info(\"" << type.name() << "\", " << "sizeof(" << type.name() << "), \"" << type.attribute("type").value() << "\");\n\n";
		}
	}
	return 0;
}


#endif
